﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CygwinPackageManager.Common;
using CygwinPackageManager.Properties;

namespace CygwinPackageManager
{
	public partial class MainForm : Form
	{
		#region Form LifeCycle
		public MainForm()
		{
			InitializeComponent();
		}

		void Application_ApplicationExit(object sender, EventArgs e)
		{
			if (!InstallationReady)
				return;

			SaveSettings();
		}

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);

			InstallationInfo installation = InstallationManager.GetInstallationInfo();

			InstallationReady = !installation.IsNew && !installation.IsCorrupt && installation.IsReady;

			if (InstallationReady)
				UpdateDataView(true, false); // UpdateDataView so layout is ready when forms gets activated.

			Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
		}

		protected override void OnActivated(EventArgs e)
		{
			base.OnActivated(e);

			if (!InstallationReady)
			{
				// Do all this stuff here so that the form is visible.
				InstallationInfo installation = InstallationManager.GetInstallationInfo();

				if ((installation.IsNew || installation.IsCorrupt) && !EditInstallationInfo())
				{
					MessageBox.Show("Cygwin not installed or corrupt. Cygwin Package Manager will now quit.");
					Application.Exit();
				}
				else if (!installation.IsReady)
				{
					this.ShowProgress("Please wait while updating installation information...");
					bwInstallationInitializer.RunWorkerAsync();
				}
			}
		}
		#endregion

		#region Event Handlers
		private void packagePredicate1_PredicateChanged(object sender, EventArgs e)
		{
			UpdatePackageGrid();
		}

		private void packageGrid1_SelectionChanged(object sender, EventArgs e)
		{
			UpdatePackageDetails();
		}

		private void packageGrid1_Action(object sender, PackageActionEventArgs e)
		{
			ApplyAction(e.PackageActionID);
		}

		#region Menu Items
		private void applyChangesToolStripMenuItem_Click(object sender, EventArgs e)
		{
			try
			{
				ApplyChanges();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		private void installationInfoToolStripMenuItem_Click(object sender, EventArgs e)
		{
			EditInstallationInfo();
		}

		private void exitToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Application.Exit();
		}

		private void filtersToolStripMenuItem_Click(object sender, EventArgs e)
		{
			SetFiltersVisibility(splitContainer2.Panel1Collapsed);
		}

		private void detailsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			SetDetailsVisibility(splitContainer1.Panel2Collapsed);
		}

		private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
		{
			using (AboutBox about = new AboutBox())
			{
				about.ShowDialog();
			}
		}

		private void printToolStripMenuItem_Click(object sender, EventArgs e)
		{
			try
			{
				PrintSelection();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		private void printPreviewToolStripMenuItem_Click(object sender, EventArgs e)
		{
			try
			{
				PrintPreviewSelection();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		private void loadToolStripMenuItem_Click(object sender, EventArgs e)
		{
			try
			{
				LoadSelection();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}


		private void saveToolStripMenuItem_Click(object sender, EventArgs e)
		{
			try
			{
				SaveSelection();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}


		#endregion

		#region Background Workers
		private void bwInstallationInitializer_DoWork(object sender, DoWorkEventArgs e)
		{
			try
			{
				InstallationManager.PrepareInstallation();
			}
			catch (Exception ex)
			{
				e.Result = ex.Message;
			}
		}

		private void bwInstallationInitializer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			string error = e.Result as string;
			if (string.IsNullOrEmpty(error))
			{
				InstallationReady = true;
				UpdateDataView(true, false);
				this.HideProgress();
			}
			else
			{
				MessageBox.Show(error);
				Application.Exit();
			}
		}
		#endregion

		#endregion

		#region Private Properties
		private bool _installationReady = false;
		private bool InstallationReady
		{
			get { return _installationReady; }
			set { _installationReady = value; }
		}

		private bool _suspendPackageGridUpdate = false;
		private bool SuspendPackageGridUpdate
		{
			get { return _suspendPackageGridUpdate; }
			set { _suspendPackageGridUpdate = value; }
		}
		#endregion

		#region Private Methods
		private void ApplyAction(int actionID)
		{
			IEnumerable<int> selectedPackageIDs = packageGrid1.SelectedPackageIDs;

			IEnumerable<PackageInfo> additionalPackages = packageManager1.GetPackagesActionAdditionalPackages(selectedPackageIDs, selectedPackageIDs, actionID);
			bool apply = additionalPackages.FirstOrDefault() == null;
			if (!apply)
			{
				using (AdditionalChangesBox box = new AdditionalChangesBox())
				{
					box.Packages = additionalPackages;
					apply = box.ShowDialog() == DialogResult.OK;
				}
			}

			if (apply)
			{
				try
				{
					packageManager1.PackageAction(selectedPackageIDs, actionID);
				}
				catch (Exception ex)
				{
					MessageBox.Show(ex.Message);
					return;
				}

				UpdatePackageGrid();
			}
		}

		private void PrintSelection()
		{
			throw new NotImplementedException();
		}
		private void PrintPreviewSelection()
		{
			throw new NotImplementedException();
		}
		private void LoadSelection()
		{
			throw new NotImplementedException();
		}
		private void SaveSelection()
		{
			throw new NotImplementedException();
		}
		private void ApplyChanges()
		{
			try
			{
				packageManager1.Apply();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		private void SetDetailsVisibility(bool visible)
		{
			splitContainer1.Panel2Collapsed = !visible;
			detailsToolStripMenuItem.Checked = visible;
		}

		private void SetFiltersVisibility(bool visible)
		{
			splitContainer2.Panel1Collapsed = !visible;
			filtersToolStripMenuItem.Checked = visible;
		}

		private void UpdatePackageDetails()
		{
			IEnumerable<int> packageIDs = packageGrid1.SelectedPackageIDs;
			IEnumerable<PackageInfo> packages = packageManager1.GetPackagesByID(packageIDs);
			IEnumerable<PackageInfo> requirements = packageManager1.GetPackagesDependencies(packageIDs, false);

			packageDetails1.Packages = packages;
			packageDetails1.Requirements = requirements;
		}

		private void UpdatePackageGrid()
		{
			if (SuspendPackageGridUpdate)
				return;

			IEnumerable<PackageInfo> packages = packageManager1.FindPackages(packagePredicate1.FreeText, packagePredicate1.SelectedStatusIDs, packagePredicate1.SelectedCategoryIDs);

			SearchableSortableBindingList<PackageInfo> packagesBindingList = new SearchableSortableBindingList<PackageInfo>();
			packagesBindingList.AddRange(packages);

			packageGrid1.Packages = packagesBindingList;
		}

		private void UpdatePackagePredicate(bool loadSettings)
		{
			packagePredicate1.Statuses = from status in packageManager1.PackageStatuses
										 orderby status.Name
										 select status;

			packagePredicate1.Categories = from category in packageManager1.PackageCategories
										   orderby category.Name
										   where !category.IsSpecial
										   select category;

			if (loadSettings)
			{
				Settings settings = Settings.Default;
				string lastCategories = settings.LastCategories;
				if (!string.IsNullOrEmpty(lastCategories))
				{
					packagePredicate1.SelectedCategoryIDs = Utils.DeSerialize(lastCategories);
				}

				string lastStatuses = settings.LastStatuses;
				if (!string.IsNullOrEmpty(lastStatuses))
				{
					packagePredicate1.SelectedStatusIDs = Utils.DeSerialize(lastStatuses);
				}
			}
		}

		private void UpdateDataView(bool loadSettings, bool resetManager)
		{
			InstallationInfo installation = InstallationManager.GetInstallationInfo();
			if (!installation.IsReady)
				throw new InvalidOperationException("Installation is not ready.");

			if (resetManager)
				packageManager1.Reset();

			lblAvailableCount.Text = packageManager1.AvailablePackages.Count().ToString();
			lblInstalledCount.Text = packageManager1.InstalledPackages.Count().ToString();

			lblRoot.Text = installation.RootDirectory;

			SuspendPackageGridUpdate = true;

			UpdatePackagePredicate(loadSettings);

			if (loadSettings)
			{
				Settings settings = Settings.Default;
				SetDetailsVisibility(settings.ShowDetails);
				SetFiltersVisibility(settings.ShowFilters);
			}

			SuspendPackageGridUpdate = false;

			UpdatePackageGrid();
		}

		private void SaveSettings()
		{
			Settings settings = Settings.Default;

			IEnumerable<int> selectedStatusIDs = packagePredicate1.SelectedStatusIDs;
			if (selectedStatusIDs != null)
			{
				settings.LastStatuses = Utils.Serialize(selectedStatusIDs);
			}

			IEnumerable<int> selectedCategoryIDs = packagePredicate1.SelectedCategoryIDs;
			if (selectedCategoryIDs != null)
			{
				settings.LastCategories = Utils.Serialize(selectedCategoryIDs);
			}

			settings.ShowFilters = !splitContainer2.Panel1Collapsed;
			settings.ShowDetails = !splitContainer1.Panel2Collapsed;

			settings.Save();
		}

		private bool EditInstallationInfo()
		{
			using (InstallationInfoBox form = new InstallationInfoBox())
			{
				DialogResult result = form.ShowDialog();
				switch (result)
				{
					case DialogResult.OK:
						UpdateDataView(false, true);
						return true;
					default:
						return false;
				}
			}
		}
		#endregion

	}
}
